using System.Windows;
using System.Windows.Controls;
using FireStarterModeller.Logic.Commands;
using FireStarterModeller.UI.DetailsView;
using FireStarterModeller.UI.DetailsView.Controller;
using FireStarterModeller.UI.DetailsView.WPF;
using Habanero.Util;
using NUnit.Framework;
using Rhino.Mocks;

namespace FireStarterModeller.Test.UI.DetailsView.WPF
{
    [TestFixture]
    public class TestSolutionAssemblyView
    {
        [Test]
        public void Test_Init()
        {
            CrossThreadRunner runner = new CrossThreadRunner();
            runner.RunInSTA
                (() =>
                 {
                     //---------------Set up test pack-------------------
                     SolutionViewModel model = new SolutionViewModel("Solution1");
                     SolutionViewModel.AssemblyModel assemblyModel = new SolutionViewModel.AssemblyModel("Assembly1");
                     SolutionAssemblyView solutionAssemblyView = new SolutionAssemblyView();
                     ISolutionViewController controller = new SolutionViewController
                         (new CommandFactory(), new CommandRunner(), model);

                     //---------------Execute Test ----------------------
                     solutionAssemblyView.Init(controller, assemblyModel);

                     //---------------Test Result -----------------------
                     Assert.AreSame(controller, solutionAssemblyView.Controller);
                     Assert.AreSame(assemblyModel, solutionAssemblyView.Model);

                     //---------------Tear Down -------------------------          
                 });
        }


        //Display assembly
        [Test]
        public void Test_DisplayShowsAllClasses()
        {
            CrossThreadRunner runner = new CrossThreadRunner();
            runner.RunInSTA
                (() =>
                 {
                     //---------------Set up test pack-------------------
                     SolutionViewModel model = new SolutionViewModel("Solution1");
                     SolutionViewModel.AssemblyModel assemblyModel = new SolutionViewModel.AssemblyModel("Assembly1");
                     assemblyModel.Classes.Add(new SolutionViewModel.ClassModel("Class1"));
                     assemblyModel.Classes.Add(new SolutionViewModel.ClassModel("Class2"));
                     assemblyModel.Classes.Add(new SolutionViewModel.ClassModel("Class3"));
                     ISolutionViewController controller = new SolutionViewController
                         (new CommandFactory(), new CommandRunner(), model);
                     SolutionAssemblyView solutionAssemblyView = new SolutionAssemblyView();

                     //---------------Execute Test ----------------------
                     solutionAssemblyView.Init(controller, assemblyModel);

                     //---------------Test Result -----------------------

                     Assert.AreEqual(4, solutionAssemblyView.ClassesPanel.Children.Count);
                 });
        }

        //Collape assembly view
        [Test]
        public void Test_HeightAfterExpanding()
        {
            CrossThreadRunner runner = new CrossThreadRunner();
            runner.RunInSTA
                (() =>
                 {
                     //---------------Set up test pack-------------------
                     SolutionViewModel model = new SolutionViewModel("Solution1");
                     SolutionViewModel.AssemblyModel assemblyModel = new SolutionViewModel.AssemblyModel("Assembly1");
                     assemblyModel.Classes.Add(new SolutionViewModel.ClassModel("Class1"));
                     assemblyModel.Classes.Add(new SolutionViewModel.ClassModel("Class2"));
                     assemblyModel.Classes.Add(new SolutionViewModel.ClassModel("Class3"));
                     ISolutionViewController controller = new SolutionViewController
                         (new CommandFactory(), new CommandRunner(), model);
                     SolutionAssemblyView solutionAssemblyView = new SolutionAssemblyView();
                     solutionAssemblyView.Init(controller, assemblyModel);

                     //---------------Execute Test ----------------------
                     solutionAssemblyView.AssemblyExpander.IsExpanded = true;

                     //---------------Test Result -----------------------

                     Assert.AreEqual
                         (SolutionAssemblyView.EXPANDER_HEIGHT + 4 * SolutionAssemblyView.CONTROL_HEIGHT,
                          solutionAssemblyView.Height);
                 });
        }

        //Collape assembly view
        [Test]
        public void Test_HeightAfterCollapsing()
        {
            CrossThreadRunner runner = new CrossThreadRunner();
            runner.RunInSTA
                (() =>
                 {
                     //---------------Set up test pack-------------------
                     SolutionViewModel model = new SolutionViewModel("Solution1");
                     SolutionViewModel.AssemblyModel assemblyModel = new SolutionViewModel.AssemblyModel("Assembly1");
                     assemblyModel.Classes.Add(new SolutionViewModel.ClassModel("Class1"));
                     assemblyModel.Classes.Add(new SolutionViewModel.ClassModel("Class2"));
                     assemblyModel.Classes.Add(new SolutionViewModel.ClassModel("Class3"));
                     ISolutionViewController controller = new SolutionViewController
                         (new CommandFactory(), new CommandRunner(), model);
                     SolutionAssemblyView assemblyView = new SolutionAssemblyView();
                     assemblyView.Init(controller, assemblyModel);
                     assemblyView.AssemblyExpander.IsExpanded = true;

                     //---------------Execute Test ----------------------
                     assemblyView.AssemblyExpander.IsExpanded = false;

                     //---------------Test Result -----------------------

                     Assert.AreEqual(SolutionAssemblyView.EXPANDER_HEIGHT, assemblyView.Height);
                 });
        }

        //Select assembly
        [Test]
        public void Test_ExpandCallsSelectAssembly()
        {
            CrossThreadRunner runner = new CrossThreadRunner();
            runner.RunInSTA
                (() =>
                 {
                     //---------------Set up test pack-------------------
                     SolutionViewModel.AssemblyModel assemblyModel = new SolutionViewModel.AssemblyModel("Assembly1");
                     MockRepository mock = new MockRepository();
                     ISolutionViewController controller = MockRepository.GenerateStub<ISolutionViewController>();
                     SolutionAssemblyView assemblyView = new SolutionAssemblyView();
                     assemblyView.Init(controller, assemblyModel);

                     controller.Expect(viewController => viewController.SelectAssembly(assemblyModel));
                     mock.ReplayAll();
                     //---------------Execute Test ----------------------
                     assemblyView.AssemblyExpander.IsExpanded = true;
                     //---------------Test Result -----------------------
                     mock.VerifyAll();
                 });
        }

        //Select assembly
        [Test]
        public void Test_CollapseCallsSelectAssembly()
        {
            CrossThreadRunner runner = new CrossThreadRunner();
            runner.RunInSTA
                (() =>
                 {
                     //---------------Set up test pack-------------------
                     SolutionViewModel.AssemblyModel assemblyModel = new SolutionViewModel.AssemblyModel("Assembly1");
                     MockRepository mock = new MockRepository();
                     ISolutionViewController controller = MockRepository.GenerateStub<ISolutionViewController>();
                     SolutionAssemblyView assemblyView = new SolutionAssemblyView();
                     assemblyView.Init(controller, assemblyModel);
                     controller.Expect(viewController => viewController.SelectAssembly(assemblyModel)).Repeat.Twice();
                     mock.ReplayAll();

                     assemblyView.AssemblyExpander.IsExpanded = true;

                     //---------------Execute Test ----------------------
                     assemblyView.AssemblyExpander.IsExpanded = false;

                     //---------------Test Result -----------------------
                     mock.VerifyAll();
                 });
        }

        //Select assembly
        //Select class
        [Test]
        public void Test_EnterClassEditorCallsSelectAssemblyAndClass()
        {
            CrossThreadRunner runner = new CrossThreadRunner();
            runner.RunInSTA
                (() =>
                 {
                     //---------------Set up test pack-------------------
                     SolutionViewModel.AssemblyModel assemblyModel = new SolutionViewModel.AssemblyModel("Assembly1");
                     SolutionViewModel.ClassModel classModel = new SolutionViewModel.ClassModel("Class1");
                     assemblyModel.Classes.Add(classModel);
                     MockRepository mock = new MockRepository();
                     ISolutionViewController controller = MockRepository.GenerateStub<ISolutionViewController>();
                     SolutionAssemblyView assemblyView = new SolutionAssemblyView();
                     assemblyView.Init(controller, assemblyModel);
                     
                     //--------------Set up expectations-----------------
                     controller.Expect(viewController => viewController.SelectAssembly(assemblyModel)).Repeat.Twice();
                     controller.Expect(viewController => viewController.SelectClass(assemblyModel, classModel));
                     mock.ReplayAll();

                     assemblyView.AssemblyExpander.IsExpanded = true;
                     TextBox classNameTextBox = (TextBox) assemblyView.ClassesPanel.Children[0];

                     //---------------Assert PreConditions---------------          
                     Assert.IsTrue(classNameTextBox.Focusable);

                     //---------------Execute Test ----------------------
                     classNameTextBox.Focus();

                     //---------------Test Result -----------------------
                     mock.VerifyAll();
                 });
        }

        //Rename assembly
        [Test]
        public void Test_RenameAssemblyModel_UpdatesDisplay()
        {
            CrossThreadRunner runner = new CrossThreadRunner();
            runner.RunInSTA
                (() =>
                 {
                     //---------------Set up test pack-------------------
                     SolutionViewModel.AssemblyModel assemblyModel = new SolutionViewModel.AssemblyModel("Assembly1");
                     MockRepository mock = new MockRepository();
                     ISolutionViewController controller = MockRepository.GenerateStub<ISolutionViewController>();
                     SolutionAssemblyView assemblyView = new SolutionAssemblyView();
                     Window window = new Window();
                     assemblyView.Init(controller, assemblyModel);
                     window.Content = assemblyView;
                     window.Show();

                     //--------------Set up expectations-----------------
                     mock.ReplayAll();

                     //---------------Assert PreConditions---------------          
                     Assert.AreEqual(assemblyModel.Name, assemblyView.AssemblyExpander.Header);

                     //---------------Execute Test ----------------------
                     assemblyModel.Name = "NewName";

                     //---------------Test Result -----------------------
                     mock.VerifyAll();
                     Assert.AreEqual(assemblyModel.Name, assemblyView.AssemblyExpander.Header);
                     //---------------Tear down--------------------------
                     window.Close();
                 });
        }


        //Select class
        [Test]
        public void Test_CurrentClassChanged_SelectsClass()
        {
            CrossThreadRunner runner = new CrossThreadRunner();
            runner.RunInSTA
                (() =>
                 {
                     //---------------Set up test pack-------------------
                     SolutionViewModel.AssemblyModel assemblyModel = new SolutionViewModel.AssemblyModel("Assembly1");
                     SolutionViewModel.ClassModel classModel = new SolutionViewModel.ClassModel("Class1");
                     assemblyModel.Classes.Add(classModel);

                     ISolutionViewController controller = CreateController();
                     SolutionAssemblyView assemblyView = new SolutionAssemblyView();
                     assemblyView.Init(controller, assemblyModel);

                     //---------------Assert PreConditions---------------  
                     Assert.IsNull(assemblyView.SelectedClassModel);

                     //---------------Execute Test ----------------------
                     assemblyModel.CurrentClass = classModel;

                     //---------------Test Result -----------------------
                     Assert.AreSame(assemblyModel.CurrentClass, assemblyView.SelectedClassModel);

                     //---------------Tear Down -------------------------         
                 });
        }


        //Create class (using current assembly)
        [Test]
        public void Test_CreateClass_WhenTypeInBlankTextBox()
        {
            CrossThreadRunner runner = new CrossThreadRunner();
            runner.RunInSTA
                (() =>
                 {
                     //---------------Set up test pack-------------------
                     SolutionViewModel.AssemblyModel assemblyModel = new SolutionViewModel.AssemblyModel("Assembly1");
                     SolutionViewModel.ClassModel classModel = new SolutionViewModel.ClassModel("Class1");
                     assemblyModel.Classes.Add(classModel);

                     MockRepository mock = new MockRepository();
                     ISolutionViewController controller = MockRepository.GenerateStub<ISolutionViewController>();

                     SolutionAssemblyView assemblyView = new SolutionAssemblyView();
                     assemblyView.Init(controller, assemblyModel);
                     const string className = "Class2";
                     TextBox class1TextBox = (TextBox) assemblyView.ClassesPanel.Children[0];

                     //--------------Setup expectations------------------
                     controller.Expect(viewController => viewController.CreateClass(assemblyModel, className));
                     mock.ReplayAll();

                     //---------------Assert PreConditions---------------    
                     Assert.AreNotSame(class1TextBox, assemblyView.NewClassTextBox);

                     //---------------Execute Test ----------------------
                     assemblyView.NewClassTextBox.Text = className;
                     assemblyView.ForceCreateClass();
                         // TODO: figure out how to test key down/lost focus event properly (tried all kinds of things already.

                     //---------------Test Result -----------------------
                     mock.VerifyAll();
                 });
        }

        //Create class (using current assembly)
        [Test]
        public void Test_CreateClass_NotCalledWhenNothingTyped()
        {
            CrossThreadRunner runner = new CrossThreadRunner();
            runner.RunInSTA
                (() =>
                 {
                     //---------------Set up test pack-------------------
                     SolutionViewModel.AssemblyModel assemblyModel = new SolutionViewModel.AssemblyModel("Assembly1");
                     SolutionViewModel.ClassModel classModel = new SolutionViewModel.ClassModel("Class1");
                     assemblyModel.Classes.Add(classModel);

                     MockRepository mock = new MockRepository();
                     ISolutionViewController controller = MockRepository.GenerateStub<ISolutionViewController>();
                     SolutionAssemblyView assemblyView = new SolutionAssemblyView();
                     assemblyView.Init(controller, assemblyModel);

                     //--------------Setup expectations------------------
                     controller.Expect(viewController => viewController.SelectAssembly(assemblyModel));
                     controller.Expect(viewController => viewController.SelectClass(assemblyModel, null));
                     mock.ReplayAll();

                     //---------------Execute Test ----------------------
                     assemblyView.NewClassTextBox.Focus();
                     assemblyView.ForceCreateClass();
                     //class1TextBox.Focus();  // make the new class text lose focus.

                     //---------------Test Result -----------------------
                     mock.VerifyAll();
                 });
        }


        //Create class (using current assembly)
        [Test]
        public void Test_ClassAddedToModel_UpdatesDisplay()
        {
            CrossThreadRunner runner = new CrossThreadRunner();
            runner.RunInSTA
                (() =>
                 {
                     //---------------Set up test pack-------------------
                     SolutionViewModel solutionViewModel = new SolutionViewModel("MySolution");
                     SolutionViewModel.AssemblyModel assemblyModel = new SolutionViewModel.AssemblyModel("Assembly1");
                     solutionViewModel.Assemblies.Add(assemblyModel);

                     ISolutionViewController controller = CreateController();
                     SolutionAssemblyView solutionAssemblyView = new SolutionAssemblyView();
                     solutionAssemblyView.Init(controller, assemblyModel);
                     solutionAssemblyView.AssemblyExpander.IsExpanded = true;

                     //---------------Assert PreConditions---------------         
                     Assert.AreEqual(1, solutionAssemblyView.ClassesPanel.Children.Count); // just the blank textbox

                     //---------------Execute Test ----------------------
                     SolutionViewModel.ClassModel classModel1 = new SolutionViewModel.ClassModel("Class1");
                     assemblyModel.Classes.Add(classModel1);

                     SolutionViewModel.ClassModel classModel2 = new SolutionViewModel.ClassModel("Class2");
                     assemblyModel.Classes.Add(classModel2);

                     //---------------Test Result -----------------------
                     Assert.AreEqual(3, solutionAssemblyView.ClassesPanel.Children.Count);

                     Assert.AreEqual(classModel1.Name, ((TextBox) solutionAssemblyView.ClassesPanel.Children[0]).Text);
                     Assert.AreEqual(classModel2.Name, ((TextBox) solutionAssemblyView.ClassesPanel.Children[1]).Text);

                     // new text box must shift downwards
                     Assert.AreSame(solutionAssemblyView.NewClassTextBox, solutionAssemblyView.ClassesPanel.Children[2]);

                     Assert.AreEqual
                         (SolutionAssemblyView.EXPANDER_HEIGHT + 3 * SolutionAssemblyView.CONTROL_HEIGHT,
                          solutionAssemblyView.Height);
                 });
        }

        //Create class (using current assembly)
        //Delete class
        [Test]
        public void Test_ClassRemovedFromModel_UpdatesDisplay()
        {
            CrossThreadRunner runner = new CrossThreadRunner();
            runner.RunInSTA
                (() =>
                 {
                     //---------------Set up test pack-------------------
                     SolutionViewModel solutionViewModel = new SolutionViewModel("MySolution");
                     SolutionViewModel.AssemblyModel assemblyModel = new SolutionViewModel.AssemblyModel("Assembly1");
                     solutionViewModel.Assemblies.Add(assemblyModel);
                     SolutionViewModel.ClassModel classModel1 = new SolutionViewModel.ClassModel("Class1");
                     assemblyModel.Classes.Add(classModel1);

                     ISolutionViewController controller = CreateController();
                     SolutionAssemblyView solutionAssemblyView = new SolutionAssemblyView();
                     solutionAssemblyView.Init(controller, assemblyModel);
                     solutionAssemblyView.AssemblyExpander.IsExpanded = true;

                     //---------------Assert PreConditions---------------         
                     Assert.AreEqual(2, solutionAssemblyView.ClassesPanel.Children.Count); // blank textbox and class1

                     //---------------Execute Test ----------------------
                     assemblyModel.Classes.Remove(classModel1);

                     //---------------Test Result -----------------------
                     Assert.AreEqual(1, solutionAssemblyView.ClassesPanel.Children.Count);

                     // new text box must shift upwards
                     Assert.AreSame(solutionAssemblyView.NewClassTextBox, solutionAssemblyView.ClassesPanel.Children[0]);

                     Assert.AreEqual
                         (SolutionAssemblyView.EXPANDER_HEIGHT + 1 * SolutionAssemblyView.CONTROL_HEIGHT,
                          solutionAssemblyView.Height);
                 });
        }

        //Delete class 
        [Test]
        public void Test_DeleteClass()
        {
            CrossThreadRunner runner = new CrossThreadRunner();
            runner.RunInSTA
                (() =>
                 {
                     //---------------Set up test pack-------------------
                     SolutionViewModel.AssemblyModel assemblyModel = new SolutionViewModel.AssemblyModel("Assembly1");
                     SolutionViewModel.ClassModel classModel = new SolutionViewModel.ClassModel("Class1");
                     assemblyModel.Classes.Add(classModel);

                     MockRepository mock = new MockRepository();
                     ISolutionViewController controller = MockRepository.GenerateStub<ISolutionViewController>();

                     SolutionAssemblyView assemblyView = new SolutionAssemblyView();
                     assemblyView.Init(controller, assemblyModel);

                     //--------------Setup expectations------------------
                     controller.Expect(viewController => viewController.DeleteClass(classModel));
                     mock.ReplayAll();

                     //---------------Execute Test ----------------------
                     assemblyView.ForceDeleteClass(classModel); // TODO: figure out how to test context menus

                     //---------------Test Result -----------------------
                     mock.VerifyAll();
                 });
        }

        //Rename class
        [Test]
        public void Test_RenameClass()
        {
            CrossThreadRunner runner = new CrossThreadRunner();
            runner.RunInSTA
                (() =>
                 {
                     //---------------Set up test pack-------------------
                     SolutionViewModel.AssemblyModel assemblyModel = new SolutionViewModel.AssemblyModel("Assembly1");
                     SolutionViewModel.ClassModel classModel = new SolutionViewModel.ClassModel("Class1");
                     assemblyModel.Classes.Add(classModel);

                     MockRepository mock = new MockRepository();
                     ISolutionViewController controller = MockRepository.GenerateStub<ISolutionViewController>();

                     SolutionAssemblyView assemblyView = new SolutionAssemblyView();
                     assemblyView.Init(controller, assemblyModel);
                     const string newName = "NewName";

                     //--------------Setup expectations------------------
                     controller.Expect(viewController => viewController.RenameClass(classModel, newName));
                     mock.ReplayAll();

                     //---------------Execute Test ----------------------
                     assemblyView.ForceRenameClass(classModel, newName); // TODO: figure out how to test context menus

                     //---------------Test Result -----------------------
                     mock.VerifyAll();
                 });
        }

        //Rename class
        [Test]
        public void Test_RenameClassModel_UpdatesDisplay()
        {
            CrossThreadRunner runner = new CrossThreadRunner();
            runner.RunInSTA
                (() =>
                 {
                     //---------------Set up test pack-------------------
                     SolutionViewModel.AssemblyModel assemblyModel = new SolutionViewModel.AssemblyModel("Assembly1");
                     SolutionViewModel.ClassModel classModel = new SolutionViewModel.ClassModel("Class1");
                     assemblyModel.Classes.Add(classModel);

                     MockRepository mock = new MockRepository();
                     ISolutionViewController controller = MockRepository.GenerateStub<ISolutionViewController>();
                     mock.ReplayAll();
                     SolutionAssemblyView assemblyView = new SolutionAssemblyView();
                     assemblyView.Init(controller, assemblyModel);
                     const string newName = "NewName";

                     TextBox classNameTextBox = (TextBox) assemblyView.ClassesPanel.Children[0];
                     //---------------Assert PreConditions---------------            
                     Assert.AreEqual(classModel.Name, classNameTextBox.Text);
                     //---------------Execute Test ----------------------
                     classModel.Name = newName;

                     //---------------Test Result -----------------------
                     Assert.AreEqual(newName, classNameTextBox.Text);
                     mock.VerifyAll();
                 });
        }

        [Test]
        public void TestMethod()
        {
            //---------------Set up test pack-------------------

            //---------------Assert PreConditions---------------            
            //---------------Execute Test ----------------------
            //---------------Test Result -----------------------
            //---------------Tear Down -------------------------          
        }

        private static ISolutionViewController CreateController()
        {
            return MockRepository.GenerateStub<ISolutionViewController>();
        }
    }
}